/*
    Author: Christopher Diggins
    License: MIT Licence 1.0
    Website: http://www.cdiggins.com
*/

#ifndef CVML_OPCODES_UTIL_HPP
#define CVML_OPCODES_UTIL_HPP

PChar OpCodeToPChar(OpCodeEnum opcode)
{
    switch (opcode)
    {
        case op_noop: return "noop";
		case op_push_flt: return "push_flt";
        case op_push_int : return "push_int";
        case op_push_short : return "push_short";
        case op_push_char : return "push_char";
        case op_push_byte : return "push_byte";
        case op_push_sub : return "push_sub";
        case op_push_sub_short : return "push_sub_short";
		case op_push_instr : return "push_instr";
        case op_push0 : return "push_0";
        case op_push1 : return "push_1";
        case op_push2 : return "push_2";
        case op_push_true : return "push_true";
        case op_push_false : return "push_false";
        case op_push_string : return "push_string";
        case op_push_neg1 : return "push_neg1";
        case op_pop: return "pop";
        case op_popd: return "popd";
        case op_pop2: return "pop2";
        case op_pop3: return "pop3";
        case op_pop4: return "pop4";
        case op_pop5: return "pop5";
        case op_pop6: return "pop6";
        case op_pop7: return "pop7";
        case op_pop8: return "pop8";
        case op_popN: return "popN";
		case op_zapN : return "zapN";
		case op_digN: return "digN";
        case op_dup: return "dup";
        case op_dupd: return "dup";
        case op_dup2: return "dup2";
        case op_dup3: return "dup3";
        case op_dupN: return "dupN";
        case op_swap: return "swap";
        case op_swapd: return "swapd";
        case op_swap1: return "swap1";
        case op_swap2: return "swap2";
        case op_swap3: return "swap3";
        case op_swapN: return "swapN";
        case op_getN: return "getN";
        case op_get1: return "get1";
        case op_get2: return "get2";
        case op_get3: return "get3";
        case op_get4: return "get4";
        case op_get5: return "get5";
        case op_get6: return "get6";
        case op_get7: return "get7";
        case op_get8: return "get8";
        case op_setN: return "setN";
        case op_set1: return "set1";
        case op_set2: return "set2";
        case op_set3: return "set3";
        case op_set4: return "set4";
        case op_set5: return "set5";
        case op_set6: return "set6";
        case op_set7: return "set7";
        case op_set8: return "set8";
        case op_ret: return "ret";
        case op_lt_int: return "lt_int";
        case op_gt_int: return "gt_int";
        case op_lteq_int: return "lteq_int";
        case op_gteq_int: return "gteq_int";
        case op_eq_int: return "eq_int";
        case op_neq_int: return "neq_int";
		case op_lt: return "lt";
        case op_gt: return "gt";
        case op_lteq: return "lteq";
        case op_gteq: return "gteq";
        case op_eq: return "eq";
        case op_neq: return "neq";
        case op_add_int: return "add_int";
        case op_sub_int: return "sub_int";
        case op_mul_int: return "mul_int";
        case op_mod_int: return "mod_int";
        case op_div_int: return "div_int";
        case op_neg_int : return "neg_int";
        case op_add_flt: return "add_flt";
        case op_sub_flt: return "sub_flt";
        case op_mul_flt: return "mul_flt";
        case op_mod_flt: return "mod_flt";
        case op_div_flt: return "div_flt";
        case op_neg_flt : return "neg_flt";
        case op_add: return "add";
        case op_sub: return "sub";
        case op_mul: return "mul";
        case op_mod: return "mod";
        case op_div: return "div";
        case op_neg : return "neg";
        case op_or : return "or";
        case op_and : return "and";
        case op_not : return "not";
        case op_xor : return "xor";
        case op_apply : return "apply";
		case op_sap : return "sap";
        case op_tail_apply : return "tail_apply";
        case op_tail_if : return "tail_if";
        case op_tail_if_else : return "tail_if_else";
        case op_quote : return "quote";
        case op_papply : return "papply";
        case op_compose : return "compose";
        case op_dip : return "dip"; 
        case op_while : return "while"; 
        case op_for : return "for";
        case op_foreach : return "foreach";
        case op_foreach_index : return "foreach_index";
        case op_forN : return "forN";
        case op_nats : return "nats";
        case op_if : return "if"; 
        case op_if_else : return "if"; 
		case op_invoke : return "invoke";
		case op_get_field : return "get_field"; 
        case op_set_field : return "set_field"; 
        case op_new : return "new"; 
        case op_new_array : return "new_array";
        case op_nil : return "nil";
        case op_count : return "count"; 
        case op_get_at : return "get_at";
        case op_set_at : return "set_at";
        case op_fold : return "fold";
        case op_map : return "map";
        case op_filter : return "filter";
        case op_filter_map : return "filter_map";
        case op_range : return "range";
        case op_empty : return "empty";
        case op_type_of : return "type_of";
        case op_external : return "lookup";
        case op_reserved : return "reserved";
        case op_lt_flt : return "lt_flt";
        case op_gt_flt : return "gt_flt";
        case op_lteq_flt : return "lteq_flt";
        case op_gteq_flt : return "gteq_flt";
        case op_eq_flt : return "eq_flt";
        case op_neq_flt : return "neq_flt";
		case op_inc : return "inc";
		case op_dec : return "dec";
		case op_repeat : return "repeat";
		case op_get_field_index : return "get_field_index";
		case op_set_field_index : return "set_field_index";
		case op_free : return "free";
		case op_null : return "null";
		case op_first : return "first";
		case op_rest : return "rest";
		case op_last : return "last";
		case op_rev : return "rev";
		case op_prepend : return "prepend";
		case op_append : return "append";
		case op_fork : return "fork";
		case op_push_sub0 : return "push_sub0";
		case op_push_sub1 : return "push_sub1";
		case op_push_sub2 : return "push_sub2";
		case op_push_sub3 : return "push_sub3";
		case op_push_sub4 : return "push_sub4";
		case op_push_sub5 : return "push_sub5";
		case op_push_sub6 : return "push_sub6";
		case op_push_sub7 : return "push_sub7";
		case op_push_sub8 : return "push_sub8";
		case op_push_sub9 : return "push_sub9";
		case op_push_sub10 : return "push_sub10";
		case op_push_sub11 : return "push_sub11";
		case op_push_sub12 : return "push_sub12";
		case op_push_sub13 : return "push_sub13";
		case op_push_sub14 : return "push_sub14";
		case op_push_sub15 : return "push_sub15";
		/*
		case op_call_sub0 : return "call_sub0";
		case op_call_sub1 : return "call_sub1";
		case op_call_sub2 : return "call_sub2";
		case op_call_sub3 : return "call_sub3";
		case op_call_sub4 : return "call_sub4";
		case op_call_sub5 : return "call_sub5";
		case op_call_sub6 : return "call_sub6";
		case op_call_sub7 : return "call_sub7";
		case op_call_sub8 : return "call_sub8";
		case op_call_sub9 : return "call_sub9";
		case op_call_sub10 : return "call_sub10";
		case op_call_sub11 : return "call_sub11";
		case op_call_sub12 : return "call_sub12";
		case op_call_sub13 : return "call_sub13";
		case op_call_sub14 : return "call_sub14";
		case op_call_sub15 : return "call_sub15";
		*/
        default:
			Error("No name assigned to opcode " + ToStr(opcode));
			return "???";
    }
}

std::string OpCodeToStr(OpCodeEnum opcode) {
	return OpCodeToPChar(opcode);
}

std::map<std::string, OpCodeEnum> InitializeOpCodeMap()
{
	std::map<std::string, OpCodeEnum> r;
	for (int i=0; i < op_reserved_final; ++i) {
		std::string s(OpCodeToPChar((OpCodeEnum)i));
		// Assert(s != "???");
		r[s] = (OpCodeEnum)i;
	}
	return r;	
}

OpCodeEnum StrToOpCode(const std::string& s) 
{
	static std::map<std::string, OpCodeEnum> map(InitializeOpCodeMap());
	return map[s];
}

int GetOpCodeWidth(OpCodeEnum code)
{
    switch (code) 
    {
    case op_noop:        return 0;
    case op_push_short: return 3;
    case op_push_int:    return 5;
    case op_push_flt:    return 5;
    case op_push_byte:    return 2;
    case op_push_char:    return 3;
    case op_push_sub:    return 3;
    case op_push_sub_short:    return 2;
        return 1;

	case op_push_instr:
		return 2; 

    case op_getN:
    case op_setN:
    case op_popN:
	case op_zapN:
    case op_dupN:
    case op_swapN:
        return 2;

    default:
        return 1;
    }
}

// Note: this is only true in the context of CVML generation
// according to the specific algorithm used in this program.
int StackModifier(OpCodeEnum opcode)
{
    switch (opcode)
    {
        case op_noop: 
            return 0;
        case op_push_int : 
        case op_push_char : 
        case op_push_byte : 
        case op_push_string :
        case op_push0 : 
        case op_push1 : 
        case op_push2 : 
        case op_push_true : 
        case op_push_false : 
        case op_push_neg1 : 
            return 1;

        case op_push_sub : 
        case op_push_sub_short : 
		case op_push_instr:
            return 1;
        case op_pop: 
		case op_zapN:
            return -1;
        case op_popd: 
            return -1;
        case op_pop2: 
            return -2;
        case op_pop3: 
            return -3;
        case op_pop4: 
            return -4;
        case op_dup: 
            return 1;
        case op_dupd: 
            return 1;
        case op_dup2: 
            return 2;
        case op_dup3: 
            return 3;
        case op_swap: 
        case op_swapd: 
        case op_swap1: 
        case op_swap2: 
        case op_swap3: 
        case op_swapN:
            return 0;
        case op_getN: 
        case op_get1: 
        case op_get2: 
        case op_get3: 
        case op_get4: 
        case op_get5: 
        case op_get6: 
        case op_get7: 
        case op_get8: 
            return 1;
        case op_setN: 
        case op_set1: 
        case op_set2: 
        case op_set3: 
        case op_set4: 
        case op_set5: 
        case op_set6: 
        case op_set7: 
        case op_set8: 
            return -1;
        case op_ret: 
            return 0;
        case op_lt: 
        case op_gt: 
        case op_lteq: 
        case op_gteq: 
        case op_eq: 
        case op_neq: 
            return -1;
        case op_add_int: 
        case op_sub_int: 
        case op_mul_int: 
        case op_mod_int: 
        case op_div_int: 
            return -1;
        case op_neg_int :
            return 0;
        case op_neg_flt : 
            return 0;
        case op_add: 
        case op_sub: 
        case op_mul: 
        case op_mod: 
        case op_div: 
            return -1;
        case op_neg : 
            return 0;
        case op_or : 
        case op_and : 
        case op_not : 
        case op_xor : 
            return -1;
        case op_apply : 
            return -1;
        case op_tail_apply : 
            return -1;

        case op_quote : 
            return 0;
        case op_papply : 
            return -1;
        case op_compose : 
            return -1;
        case op_dip : 
            return -1;
        case op_while : 
            return -2;
        case op_for :
            return -3;
        case op_foreach :
            return -2;
        case op_foreach_index :
            return -2;
        case op_forN :
            return -2;
        case op_if : 
            return -3;
        case op_tail_if :
            return -2;
        case op_get_field : 
            return -1;
        case op_set_field : 
            return -1;
        case op_new : 
            return 0;
        case op_new_array : 
            return 1;
        case op_nil : 
            return 1;
        case op_count : 
            return 1; 
        case op_get_at : 
            return -1;
        case op_set_at : 
            return -3;
        case op_fold : 
            return -2;
        case op_map : 
            return -1;
        case op_filter : 
            return -1;
        case op_filter_map : 
            return -2;
        case op_range : 
            return -2;
        case op_empty : 
            return 1;
        case op_type_of : 
            return 1;
		default:
            Error("unrecognized opcode");
    }
    return 0;
}

bool IsSubTerminator(OpCodeEnum opcode)
{
	switch (opcode) 
	{
		case op_ret:
		case op_tail_apply:		
		case op_tail_if_else:		
		case op_tail_if:
			return true;
		default:
			return false;
	}
}

#endif
